

namespace reqengine {

class RequestNode;
class RequestElement;

class Request {
  typedef std::list<RequestNode *> RequestNodeList;

 public:
  Request();
  ~Request();

  void Initialize(RequestContext *context, ResponseContext *res);

  void DoRequest() {
    this->request_context_->DivideNode();
    size_t size = this->request_context_->DivideNodeSize();
    for(size_t pos = 0; pos < size; ++pos) {
      RequestNode *node = RequestEngine::GetInstance()->CreateRequestNode();
      node->Initialize(this->request_context_->GetNodeContext(pos));
      this->requests_->push_back(node);
      if(pos == 0) node->OnRequest();
    }
  }
  void OnResponse() {
    RequestNode *node = this->requests_.pop();
    this->response_context_->Absorb(node->GetResponseContext());
    if(requests_.empty()) {
      this->request_context_->Response(this->response_context_);
    }
  }
 
 private:
  RequestNodeList requests_;
  RequestContext *request_context_;
  ResponseContext *response_context_;
};

class RequestNode {
  typedef std::map<uint64, RequestElement *> RequestElementMap;

 public:
  RequestNode();
  ~RequestNode();

  void Initialize(RequestContext *req, ResponseContext *res);

  void DoRequest() {
    this->request_context_->DivideElement();
    size_t size = this->request_context_->DivideElementSize();
    for(size_t pos = 0; pos < size; ++size) {
      RequestElement *element = RequestEngine::GetInstance()->CreateRequestElement();
      element->Initialize(this->request_context_->GetElementContext(pos));
      this->requests_.insert(std::make_pair(element->GetID(), element));
      element->DoRequest();
    }
  }

  void OnResponse(uint64 id) {
    RequestElementMap::iterator iterator = this->requests_.find(id);
    if(iterator != this->requests_.end()) {
      RequestElement *element = iterator->second;
      this->response_context_->Merge(element->GetResponseContext());
      this->requests_.erase(iterator);
      RequestEngine::GetInstance()->RecycleRequestElement(element);
    }
    if(this->requests_.empty()) {
      this->master_->OnResponse();
    }
  }

 private:
  Request *master_;
  RequestContext *request_context_;
  ResponseContext *response_context_;
  uint32 request_number_;
  RequestElementMap requests_;
};

class RequestElement {
 public:
  RequestElement();
  ~RequestElement();

  void Initialize(RequestContext *context);

  void DoRequest() {
    this->request_context_->Request();
    RequestEngine::GetInstance()->OnRequest(this);
  }
  void OnResponse(ResponseContext *context) {
    this->response_context_ = context;
    this->master_->OnResponse(id_);
  }

 private:
  RequestNode *master_;
  uint64 id_;

  RequestContext *request_context_;
  ResponseContext *response_context_;
  bool invalid_;
};


class RequestContext {
 public:
  RequestContext();
  ~RequestContext();

  void Initialize(TCPConnection *, HttpRequest *);

  void DivideNode();
  size_t DivideNodeSize();
  RequestContext *GetNodeContext(size_t);

  void DivideElement();
  size_t DivideElementSize();
  RequestContext *GetElementContext(size_t);

  void SendRequest() {
    if(this->connection_) {
      this->connection_->SendMessage(/*data, size*/);
    }
  }

  void Request() {
    if(this->loop_->CheckInLoopThread()) {
      this->SendRequest();
    } else {
      EventHandler *hander = new EventHandler();
      hander->SetEventCallback(boost::bind(&RequestContext::SendRequest, this));
      loop->SetEvent(hander);
    }
  }

  void Response(ResponseContext *context) {
    this->response_ = context;
    if(this->loop_->CheckInLoopThread()) {
      this->Response();
    } else {
      EventHandler *hander = new EventHandler();
      hander->SetEventCallback(boost::bind(&RequestContext::Response, this));
      loop->SetEvent(hander);
    }
  }

  void Response() {
    if(this->connection_) {
      HttpResponse response(context->GetHttpResponse());
      this->connection_->SendMessage(HttpResponse.Data(), HttpResponse.size());
      RequestEngine::GetInstance()->RecycleRequestContext(this);
    }
  }

  void SetConnectionBad() {
    this->connection_ = NULL;
  }

 private:
  TCPConnection *connection_;
  EventLoop *loop_;
  ResponseContext *response_;
}

/*
  TCPConnection::context -> HttpContext -> RequestContext;
  TCPConnection::OnConnectionDestoryed() [
    boost_any<HttpContext *>(this->context)->SetConnectionBad();
  }
 */

}  // namespace reqengine

